﻿using System.Linq;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UMC.Data;
using UMC.Net;
using UMC.Web;
using System.Buffers;
using System.Threading.Channels;
using System.Threading.Tasks;


namespace UMC.ITME
{
    public class NetBridgeClient : NetBridge
    {
        public int ConfigKey
        {
            get; set;
        }
        public override void Close()
        {
            Clients.Clear();
            base.Close();

            if (this.ConfigKey != 0)
            {
                UMC.Bridge.HttpBridge.Statistical(this.ConfigKey, this.SendQty, this.ReceiveQty);
            }
        }
        protected static ConcurrentDictionary<int, INetBridgeReader> Clients = new ConcurrentDictionary<int, INetBridgeReader>();

        protected override void Prepare(int pid, byte[] buffer, int index, int length)
        {
            if (length > 0)
            {
                switch (buffer[index])
                {
                    case 0x05:
                        Clients[pid] = new Socks5Server(this.Write(pid), new StringValue(buffer.AsSpan(index + 1, length - 1).ToArray()));
                        break;
                    case 0x02:
                        Clients[pid] = new HttpBridgeMime(this.Write(pid), new StringValue(buffer.AsSpan(index + 1, length - 1).ToArray()));
                        break;
                    case 0x03:
                        Clients[pid] = new HttpBridgeProxy(this.Write(pid), new StringValue(buffer.AsSpan(index + 1, length - 1).ToArray()));
                        break;
                }
            }
        }
        protected override void Read(int pid, byte[] buffer, int index, int length)
        {
            if (length == 0)
            {
                if (Clients.TryRemove(pid, out var mimeBody))
                {
                    mimeBody.Receive(buffer, index, length);
                }
            }
            else if (Clients.TryGetValue(pid, out var proxy))
            {
                try
                {
                    proxy.Receive(buffer, index, length);
                    if (proxy.IsOver)
                    {
                        Clients.TryRemove(pid, out proxy);
                    }
                }
                catch //(Exception ex)
                {
                    if (proxy is IDisposable)
                    {
                        ((IDisposable)proxy).Dispose();
                    }
                    Clients.TryRemove(pid, out var _);
                    this.Write(pid, Array.Empty<byte>(), 0, 0);
                }
            }
        }


    }
    class HttpBridgeClient : NetBridgeClient, UMC.Host.IDoWorker
    {

        public virtual void Abort()
        {

            this.Close();

        }
        private static ConcurrentDictionary<int, HttpBridgeClient> _bridgeClients = new ConcurrentDictionary<int, HttpBridgeClient>();

        public static void Start(Uri bridge)
        {
            var ipAddresss = Dns.GetHostAddresses(bridge.Host);

            if (_bridgeClients.Count > 0)
            {
                var vs = _bridgeClients.Values.ToArray();
                foreach (var vi in vs)
                {
                    vi.Close();
                }
                _bridgeClients.Clear();

            }
            Stop();
            for (var i = 0; i < 2; i++)
            {
                try
                {
                    foreach (var ip in ipAddresss)
                    {
                        Connect(bridge.Host, ip, bridge.Port, i, true);
                    }
                }
                catch
                {
                    break;
                }
            }

        }
        String host;
        IPAddress address;
        int port, index;

        static async void Connect(String host, IPAddress ip, int port, int index, bool start)
        {
            var client = new Socket(SocketType.Stream, ProtocolType.Tcp);
            var buffers = ArrayPool<byte>.Shared.Rent(64);// new byte[2000];
            try
            {
                await client.ConnectAsync(ip, port);
                buffers[0] = 0x02;
                buffers[1] = 0x00;
                if (index == 0 && start)
                {
                    buffers[2] = 0xFF;
                }
                else
                {
                    buffers[2] = (byte)index;
                }
                buffers[3] = (byte)host.WriteBytes(buffers, 4);
                buffers[buffers[3] + 4] = 0x00;//(byte)host.WriteBytes(buffers, 4);
                await client.SendAsync(new ArraySegment<byte>(buffers, 0, buffers[3] + 5));
                await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, 2));
                if (buffers[0] != 0x00)
                {
                    client.Close();
                    client.Dispose();
                    return;
                }
                await client.ReceiveAsync(new ArraySegment<byte>(buffers, 0, buffers[1] + 1));
                var bridgeClient = new HttpBridgeClient
                {
                    host = host,
                    address = ip,
                    index = index,
                    port = port
                };
                bridgeClient.Bridge(client);

                _bridgeClients.TryAdd(bridgeClient.GetHashCode(), bridgeClient);

            }
            catch
            {

            }
            finally
            {
                ArrayPool<byte>.Shared.Return(buffers);
            }

        }

        public static String ServerChange(int states)
        {
            var sb = new StringBuilder();
            switch (states % 3)
            {
                case 0:
                    {

                        var secret = UMC.Data.WebResource.Instance().Provider["appSecret"];

                        if (String.IsNullOrEmpty(secret))
                        {
                            sb.AppendLine("获取失败：\a应用未注册，请注册应用。");
                        }
                        else
                        {
                            var webr = new Uri(APIProxy.Uri, "Transfer").WebRequest();

                            // var ns = new System.Collections.Specialized.NameValueCollection();

                            UMC.ITME.Utility.Sign(webr, secret);
                            try
                            {
                                var meta = JSON.Deserialize<WebMeta>(webr.Get(out var _));
                                if (meta.ContainsKey("msg"))
                                {
                                    sb.AppendLine($"获取失败：\a{meta["msg"]}。");

                                }
                                else
                                {
                                    var bridgeUrl = meta["bridge"];


                                    if (IsRunning)
                                    {
                                        sb.AppendLine($"服务状态：\b已连接");
                                    }
                                    else
                                    {
                                        sb.AppendLine($"服务状态：未连接");
                                    }
                                    sb.AppendLine($"转发网址：{bridgeUrl}");



                                    sb.AppendLine($"流量过期：{meta["expireTime"]}");
                                    sb.AppendLine($"剩余流量：{meta["allowSize"]}");
                                    sb.AppendLine($"上行流量：{meta["inputSize"]}");
                                    sb.AppendLine($"下行流量：{meta["outputSize"]}");

                                    sb.AppendLine();
                                    sb.AppendLine("特别注意：\a过期后剩余流量将会清零!!!");

                                }
                            }
                            catch //(Exception ex)
                            {
                                sb.AppendLine($"转发服务：\a未连网");
                            }
                        }
                    }
                    break;
                case 1:
                    if (_bridgeClients.Count > 0)
                    {
                        sb.AppendLine("正在关停转发服务。");
                        Stop();
                    }
                    else
                    {
                        sb.AppendLine("转发服务未开启。");
                    }
                    break;
                case 2:
                    {
                        var secret = UMC.Data.WebResource.Instance().Provider["appSecret"];
                        if (String.IsNullOrEmpty(secret))
                        {
                            sb.AppendLine($"转发服务开启失败!!!");
                            sb.AppendLine("失败原因：\a应用未注册，请注册应用。");
                        }
                        else if (_bridgeClients.Count > 0)
                        {
                            return ServerChange(0);
                        }
                        else
                        {
                            var webr = new Uri(APIProxy.Uri, "Transfer").WebRequest();

                            UMC.ITME.Utility.Sign(webr, secret);
                            try
                            {
                                var result = webr.Get(out var xhr);
                                if (xhr.StatusCode == HttpStatusCode.OK)
                                {

                                    var meta = JSON.Deserialize<WebMeta>(result) ?? new WebMeta();

                                    if (meta.ContainsKey("bridge") == false)
                                    {
                                        sb.AppendLine($"转发服务开启失败!!!");
                                        sb.AppendLine();
                                        sb.AppendLine("失败原因：\a域名未注册，请注册域名。");
                                    }
                                    else if (meta.ContainsKey("msg"))
                                    {
                                        sb.AppendLine($"转发服务开启失败!!!");
                                        sb.AppendLine();
                                        sb.AppendLine($"失败原因：\a{meta["msg"]}。");

                                    }
                                    else
                                    {


                                        var bridgeUrl = meta["bridge"];

                                        Start(new Uri(bridgeUrl));
                                        sb.AppendLine($"转发网址：\b{bridgeUrl}");
                                        sb.AppendLine($"服务状态：\a正连接");

                                        sb.AppendLine($"剩余流量：{meta["allowSize"]}");
                                        sb.AppendLine($"上行流量：{meta["inputSize"]}");
                                        sb.AppendLine($"下行流量：{meta["outputSize"]}");


                                        sb.AppendLine($"过期天数：{meta["expireTime"]}");
                                        sb.AppendLine();
                                        sb.AppendLine($"特别注意：\a过期后剩余流量将会清零!!!");

                                        var provider = Data.WebResource.Instance().Provider;
                                        if (String.Equals(provider.Attributes["bridge"], bridgeUrl) == false)
                                        {
                                            provider.Attributes["bridge"] = bridgeUrl;

                                            var key = meta["key"];
                                            var pdomain = provider["domain"];
                                            var domain = meta["domain"];
                                            if (String.IsNullOrEmpty(pdomain) || pdomain.StartsWith(key))
                                            {
                                                if (meta["union"] == "-")
                                                {
                                                    provider.Attributes["union"] = "-";
                                                    WebServlet.DomainUnion = '-';
                                                }
                                                provider.Attributes["scheme"] = "https";
                                                provider.Attributes["domain"] = domain;
                                                UMC.ITME.WebServlet.MainDomain = domain;
                                            }
                                        }

                                        provider.Attributes["repost"] = "open";
                                        var pc = Reflection.Configuration("assembly") ?? new ProviderConfiguration();
                                        pc.Add(provider);
                                        Reflection.Configuration("assembly", pc);

                                    }
                                }
                                else if (xhr.Error != null)
                                {
                                    sb.AppendLine($"\r转发服务开启失败!!!");
                                    sb.AppendLine();
                                    sb.AppendLine($"失败原因：\a{xhr.Error.Message}");
                                }
                                else
                                {
                                    sb.AppendLine($"\r转发服务开启失败!!!");
                                    sb.AppendLine();
                                    sb.AppendLine($"失败原因：\a{result}");

                                }
                            }
                            catch (Exception ex)
                            {

                                sb.AppendLine($"\rWeb VPN开启失败!!!");
                                sb.AppendLine();

                                sb.AppendLine($"失败原因：\r{ex.Message}");
                            }

                        }
                    }
                    break;
            }
            return sb.ToString();
        }
        public static bool IsRunning
        {
            get
            {
                var client = new TcpClient();
                var bs = new byte[16];
                try
                {

                    client.Connect(NetProxy.DNSToIPAsync("api.apiumc.com").Result.First(), 443);
                    var st = client.GetStream();
                    bs[0] = 0x08; // 协议版本
                    bs[1] = 0;
                    bs[2] = 0;
                    bs[3] = 0;
                    bs[4] = 0;
                    bs[5] = 4;
                    // UMC.ITME.WebServlet.AppIdKey.Span.CopyTo(bs.AsSpan(6));
                    // bs.AsSpan(6, 4).Reverse();
                    System.Buffers.Binary.BinaryPrimitives.WriteInt32BigEndian(bs.AsSpan(6), Utility.IntParse(UMC.ITME.WebServlet.AppId.Span));
                    st.Write(bs, 0, 11);
                    int size = st.Read(bs, 0, 4);
                    if (size == 4)
                    {
                        if (System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(bs.AsSpan(0)) + 500 > Utility.TimeSpan())
                        {
                            // this.Prompt("转发服务在线");
                            return true;
                        }
                        else
                        {
                            // this.Prompt("转发服务不在线");
                            return false;
                        }
                        // this.PromF
                    }
                }
                catch (SocketException)
                {
                    // this.Prompt("转发服务不在线");
                    // return;
                }
                finally
                {
                    client.Close();

                }
                return false;
            }
        }
        public static void Stop()
        {
            lock (_bridgeClients)
            {
                var cls = _bridgeClients.Values.ToArray();
                for (var i = 0; i < cls.Length; i++)
                {
                    try
                    {
                        cls[i].Abort();
                    }
                    catch
                    {

                    }
                }
                _bridgeClients.Clear();
            }

            var provider = Data.WebResource.Instance().Provider;
            provider.Attributes["repost"] = "off";
            var pc = Reflection.Configuration("assembly") ?? new ProviderConfiguration();
            pc.Add(provider);
            Reflection.Configuration("assembly", pc);
        }

        public override void Close()
        {
            base.Close();


            _bridgeClients.TryRemove(this.GetHashCode(), out var _);

            HttpMimeServier.Register(10, this);

        }
        public static void Check()
        {
            var now = Utility.TimeSpan();
            var vs = _bridgeClients.Values.ToArray();
            foreach (var b in vs)
            {
                if (now > b.ActiveTime + 10)
                {
                    b.Ping();
                }

            }
        }

        void Host.IDoWorker.DoWork()
        {
            try
            {
                Connect(host, address, port, this.index, false);
            }
            catch
            {
                HttpMimeServier.Register(30, this);
            }
        }
    }


}
